<?php

namespace stlswm\WxSubscriptionPHP;


/**
 * Class CallbackMonitor
 * 公众平台回调监听器
 *
 * @package WxSubscriptionPHP
 * @Date    2018/12/29
 * @Time    10:39
 */
class CallbackMonitor
{
    /**
     * @var string 认证授权码
     */
    public $authToken;

    /**
     * @var array 公众平台请求参数
     */
    private $input;

    /**
     * @var callable 输出echostr回调
     */
    public $echoStrHandler;

    /**
     * @var callable 授权验证完成回调
     */
    public $afterAuthHandler;

    /**
     * @var callable 文本消息回调
     */
    public $textMsgHandler;

    /**
     * @var callable 图片消息回调
     */
    public $imageMsgHandler;

    /**
     * @var callable 语音消息回调
     */
    public $voiceMsgHandler;

    /**
     * @var callable 视频消息回调
     */
    public $videoMsgHandler;

    /**
     * @var callable 小视频消息回调
     */
    public $shortVideoMsgHandler;

    /**
     * @var callable 地理位置消息回调
     */
    public $locationMsgHandler;

    /**
     * @var callable 链接消息回调
     */
    public $linkMsgHandler;

    /**
     * @var callable 关注事件回调
     */
    public $subscribeEventHandler;

    /**
     * @var callable 取消关注事件回调
     */
    public $unSubscribeEventHandler;

    /**
     * @var callable 扫码事件回调
     */
    public $scanEventHandler;

    /**
     * @var callable 地理位置上报事件回调
     */
    public $locationEventHandler;

    /**
     * @var callable 自定义菜单点击回调
     */
    public $menuClickEventHandler;
    /**
     * @var callable 点击菜单跳转链接时的事件推送回调
     */
    public $menuViewEventHandler;

    /**
     * @var callable 扫码推事件的事件推送回调
     */
    public $menuScanCodePushEventHandler;

    /**
     * @var callable 扫码推事件且弹出“消息接收中”提示框的事件推送回调
     */
    public $menuScanCodeWaitMsgHandler;

    /**
     * @var callable 弹出系统拍照发图的事件推送回调
     */
    public $menuPicSysPhotoHandler;

    /**
     * @var callable 弹出拍照或者相册发图的事件推送回调
     */
    public $menuPicPhotoOrAlbum;

    /**
     * @var callable 弹出微信相册发图器的事件推送回调
     */
    public $menuPicWeiXinHandler;

    /**
     * @var callable 弹出地理位置选择器的事件推送回调
     */
    public $menuLocationSelectHandler;

    /**
     * @var callable 模板消息发送状态报告回调
     */
    public $templateSendJobFinishHandler;

    /**
     * 验证AuthToken
     *
     * @throws \Exception
     * @Author wm
     * @Date   2018/12/29
     * @Time   10:57
     */
    private function checkAuthToken()
    {
        $mustFields = [
            'signature',
            'timestamp',
            'nonce',
        ];
        foreach ($mustFields as $field) {
            if (!isset($_REQUEST[$field])) {
                throw new \Exception('missing param ' . $field);
            }
        }
        $tmpArr = [
            $this->authToken,
            $_REQUEST["timestamp"],
            $_REQUEST["nonce"],
        ];
        sort($tmpArr, SORT_STRING);
        $tmpStr = sha1(implode($tmpArr));
        if ($tmpStr != $_REQUEST['signature']) {
            throw new \Exception('auth fail');
        }
        if (isset($_REQUEST['echostr'])) {
            if (is_callable($this->echoStrHandler)) {
                call_user_func($this->echoStrHandler, $_REQUEST['echostr']);
            }
        }
    }

    /**
     * @return bool
     * @Author wm
     * @Date   2018/12/29
     * @Time   11:04
     */
    private function parseRequestBody(): bool
    {
        if (strtolower($_SERVER['REQUEST_METHOD']) != 'post') {
            return false;
        }
        $input = file_get_contents('php://input');
        $this->input = Tools::xmlToArray($input);
        return true;
    }

    /**
     * 开始回调监听
     *
     * @throws \Exception
     * @Author wm
     * @Date   2018/12/29
     * @Time   10:55
     */
    public function listen()
    {
        if (empty($this->authToken)) {
            throw new \Exception('$authToken can not empty');
        }
        //验证AuthToken
        $this->checkAuthToken();
        //解析请求参数
        $bool = $this->parseRequestBody();
        if (!$bool) {
            return;
        }
        if (is_callable($this->afterAuthHandler)) {
            call_user_func($this->afterAuthHandler, $this->input);
        }
        switch ($this->input['MsgType']) {
            case 'text':
                if (is_callable($this->textMsgHandler)) {
                    call_user_func($this->textMsgHandler, $this->input);
                }
                break;
            case 'image':
                if (is_callable($this->imageMsgHandler)) {
                    call_user_func($this->imageMsgHandler, $this->input);
                }
                break;
            case 'voice':
                if (is_callable($this->voiceMsgHandler)) {
                    call_user_func($this->voiceMsgHandler, $this->input);
                }
                break;
            case 'video':
                if (is_callable($this->videoMsgHandler)) {
                    call_user_func($this->videoMsgHandler, $this->input);
                }
                break;
            case 'shortvideo':
                if (is_callable($this->shortVideoMsgHandler)) {
                    call_user_func($this->shortVideoMsgHandler, $this->input);
                }
                break;
            case 'link':
                if (is_callable($this->linkMsgHandler)) {
                    call_user_func($this->linkMsgHandler, $this->input);
                }
                break;
            case 'event':
                switch ($this->input['Event']) {
                    case 'subscribe':
                        //订阅
                        if (is_callable($this->subscribeEventHandler)) {
                            call_user_func($this->subscribeEventHandler, $this->input);
                        }
                        if (!empty($this->input['EventKey'])) {
                            $this->input['EventKey'] = str_replace('qrscene_', '', $this->input['EventKey']);
                            if (is_callable($this->scanEventHandler)) {
                                call_user_func($this->scanEventHandler, $this->input);
                            }
                        }
                        break;
                    case 'unsubscribe':
                        //取消订阅
                        if (is_callable($this->unSubscribeEventHandler)) {
                            call_user_func($this->unSubscribeEventHandler, $this->input);
                        }
                        break;
                    case 'SCAN':
                        //扫描带参数二维码事件
                        if (is_callable($this->scanEventHandler)) {
                            call_user_func($this->scanEventHandler, $this->input);
                        }
                        break;
                    case 'LOCATION':
                        //上报地理位置事件
                        if (is_callable($this->locationEventHandler)) {
                            call_user_func($this->locationEventHandler, $this->input);
                        }
                        break;
                    case 'CLICK':
                        //点击菜单拉取消息时的事件推送
                        if (is_callable($this->menuClickEventHandler)) {
                            call_user_func($this->menuClickEventHandler, $this->input);
                        }
                        break;
                    case 'VIEW':
                        //点击菜单跳转链接时的事件推送
                        if (is_callable($this->menuViewEventHandler)) {
                            call_user_func($this->menuViewEventHandler, $this->input);
                        }
                        break;
                    case 'scancode_push':
                        //扫码推事件的事件推送
                        if (is_callable($this->menuScanCodePushEventHandler)) {
                            call_user_func($this->menuScanCodePushEventHandler, $this->input);
                        }
                        break;
                    case 'scancode_waitmsg':
                        //扫码推事件且弹出“消息接收中”提示框的事件推送
                        if (is_callable($this->menuScanCodeWaitMsgHandler)) {
                            call_user_func($this->menuScanCodeWaitMsgHandler, $this->input);
                        }
                        break;
                    case 'pic_sysphoto':
                        //弹出系统拍照发图的事件推送
                        if (is_callable($this->menuPicSysPhotoHandler)) {
                            call_user_func($this->menuPicSysPhotoHandler, $this->input);
                        }
                        break;
                    case 'pic_photo_or_album':
                        //弹出拍照或者相册发图的事件推送
                        if (is_callable($this->menuPicPhotoOrAlbum)) {
                            call_user_func($this->menuPicPhotoOrAlbum, $this->input);
                        }
                        break;
                    case 'pic_weixin':
                        //弹出微信相册发图器的事件推送
                        if (is_callable($this->menuPicWeiXinHandler)) {
                            call_user_func($this->menuPicWeiXinHandler, $this->input);
                        }
                        break;
                    case 'location_select':
                        //弹出微信相册发图器的事件推送
                        if (is_callable($this->menuLocationSelectHandler)) {
                            call_user_func($this->menuLocationSelectHandler, $this->input);
                        }
                        break;
                    case 'TEMPLATESENDJOBFINISH':
                        //模板消息发送状态
                        if (is_callable($this->templateSendJobFinishHandler)) {
                            call_user_func($this->templateSendJobFinishHandler, $this->input);
                        }
                        break;
                }
                break;
        }
    }
}